#ifndef __BINARY_IO_H__
#define __BINARY_IO_H__

#include <cstdint>
#include <string>
#include <ostream>
#include <istream>
#include <vector>

namespace Utils
{
    class BinaryReaderBase
    {
    public:
        virtual char readChar() = 0;
        virtual void read(char *buff, uint32_t readLength) = 0;
        virtual uint32_t getPosition() = 0;
        virtual void setPosition(uint32_t position) = 0;
        int64_t readInt64();
        int32_t readInt32();
        int16_t readInt16();
        int8_t readInt8();
        uint64_t readUInt64();
        uint32_t readUInt32();
        uint16_t readUInt16();
        uint8_t readUInt8();
        bool readBoolen();
        double readDouble();
        float readFloat();
        void readSizeString(std::string &value);
        int32_t read7BitEncodedInt();
        void readString(std::string &value);
    };

    class StreamBinaryReader : public BinaryReaderBase
    {
    public:
        StreamBinaryReader(std::istream *stream);
        virtual char readChar() override;
        virtual void read(char *buff, uint32_t readLength) override;
        virtual uint32_t getPosition() override;
        virtual void setPosition(uint32_t position) override;

    protected:
        std::istream *m_stream;
    };

    class MemoryBinaryReader : public BinaryReaderBase
    {
    public:
        MemoryBinaryReader(const char *buffer, uint32_t buffSize = -1);
        virtual char readChar() override;
        virtual void read(char *buff, uint32_t readLength) override;
        virtual uint32_t getPosition() override;
        virtual void setPosition(uint32_t position) override;
        inline uint32_t getBuffSize()
        {
            return m_buffSize;
        }
        inline const char *getBuff() const
        {
            return m_buffer;
        }
        inline const char *getBuffNowPtr() const
        {
            return &m_buffer[m_position];
        }

    protected:
        const char *m_buffer;
        uint32_t m_position;
        uint32_t m_buffSize;
    };

    class BinaryWriterBase
    {
    public:
        virtual ~BinaryWriterBase();
        virtual void write(const char *buff, uint32_t length) = 0;
        virtual uint32_t getPosition() = 0;
        virtual void setPosition(uint32_t position) = 0;

        void writeChar(char value);
        void writeInt64(int64_t value);
        void writeInt32(int32_t value);
        void writeInt16(int16_t value);
        void writeInt8(int8_t value);
        void writeUInt64(uint64_t value);
        void writeUInt32(uint32_t value);
        void writeUInt16(uint16_t value);
        void writeUInt8(uint8_t value);
        void writeBoolean(bool value);
        void writeDouble(double value);
        void writeFloat(float value);
        void writeSizeString(const std::string &value);
        void write7BitEncodedInt(int32_t value);
        void writeString(const std::string &text);
    };

    class StreamBinaryWriter : public BinaryWriterBase
    {
    public:
        StreamBinaryWriter(std::ostream *stream);
        virtual void write(const char *buff, uint32_t length) override;
        virtual uint32_t getPosition() override;
        virtual void setPosition(uint32_t position) override;

    protected:
        std::ostream *m_stream;
    };
    class MemoryBinaryWriter : public BinaryWriterBase
    {
    public:
        MemoryBinaryWriter(char *buffer, uint32_t buffSize = -1);
        virtual void write(const char *buff, uint32_t length) override;
        virtual uint32_t getPosition() override;
        virtual void setPosition(uint32_t position) override;
        inline uint32_t getBuffSize()
        {
            return m_buffSize;
        }

    protected:
        char *m_buffer;
        uint32_t m_position;
        uint32_t m_buffSize;
    };
    class DynamicBinaryWriter : public BinaryWriterBase
    {
    public:
        DynamicBinaryWriter(uint32_t buffSize = 1024);
        ~DynamicBinaryWriter();
        virtual void write(const char *buff, uint32_t length) override;
        virtual uint32_t getPosition() override;
        virtual void setPosition(uint32_t position) override;
        inline uint32_t getBuffSize()
        {
            return m_buffSize;
        }
        inline char *getBuffer()
        {
            return m_buffer;
        }

    protected:
        char *m_buffer;
        uint32_t m_position;
        uint32_t m_buffSize;
    };
}

#endif